﻿//==============================================================================
// File: ManyToOneAssocSync.cs
// Created: 2010-05-10
// Author: Piotr Włodek
//==============================================================================
// This file is a part of MAMMOTH project.
// Copyright (C) 2010 AGH University of Science and Technology, Krakow.
// https://caribou.iisg.agh.edu.pl/trac/mammoth
//
// Mammoth is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// Mammoth is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with WpfRcp. If not, see http://www.gnu.org/licenses/.
//==============================================================================

using System;
using System.Collections;

namespace WpfRcp.PropertyModel.Internal.Associations
{
    /// <summary>
    /// Keeps both sides of a bidirectional many-to-one association in sync with each other.
    /// </summary>
    internal static class ManyToOneAssocSync 
    {
        /// <summary>
        /// Responds to a many-side entity's parent one-side property being set to a new value.
        /// </summary>
        public static void ResynchronizeOneSide(PropertyContainer manySide, MetaProperty manyToOneProperty, MetaProperty oneToManyProperty, PropertyContainer oneSide)
        {
            if (!manySide.IsSynchronizing(manyToOneProperty))
            {
                using (manySide.BlockSynchronizationReentrency(manyToOneProperty))
                {
                    if (oneSide != null && !oneSide.IsSynchronizing(oneToManyProperty))
                    {
                        var oneSideValue = oneSide.GetValue(oneToManyProperty);

                        // Other side may be null if collections are during first initialization
                        if (oneSideValue == null)
                            return;

                        var oneSideCollection = oneSideValue as IList;
                        if (oneSideCollection == null)
                            throw new ArgumentException(
                                "To use ManyToOne sychronization other property must be of type that implements IList");

                        if (!oneSideCollection.Contains(manySide))
                        {
                            oneSideCollection.Add(manySide);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Responds to a many-side entity's parent one-side property being set to a new value.
        /// </summary>
        public static void UpdateOneSide(PropertyContainer manySide, MetaProperty manyToOneProperty, MetaProperty oneToManyProperty, PropertyContainer oldOneSide, PropertyContainer newOneSide)
        {
            if (!manySide.IsSynchronizing(manyToOneProperty))
            {
                using (manySide.BlockSynchronizationReentrency(manyToOneProperty))
                {
                    if (oldOneSide != null && oldOneSide != newOneSide)
                    {
                        if (!oldOneSide.IsSynchronizing(oneToManyProperty))
                        {
                            var oldValue = oldOneSide.GetValue(oneToManyProperty);
                            // Other side may be null if collections are during first initialization
                            if (oldValue == null)
                                return;

                            var oldCollection = oldValue as IList;
                            if (oldCollection == null)
                                throw new ArgumentException(
                                    "To use ManyToOne sychronization other property must be of type that implements IList");

                            if (oldCollection.Contains(manySide))
                            {
                                oldCollection.Remove(manySide);
                            }
                        }
                    }

                    if (newOneSide != null)
                    {
                        if (!newOneSide.IsSynchronizing(oneToManyProperty))
                        {
                            var newValue = newOneSide.GetValue(oneToManyProperty);
                            // Other side may be null if collections are during first initialization
                            if (newValue == null)
                                return;

                            var newCollection = newValue as IList;
                            if (newCollection == null)
                                throw new ArgumentException(
                                    "To use ManyToOne sychronization other property must be of type that implements IList");

                            if (!newCollection.Contains(manySide))
                            {
                                newCollection.Add(manySide);
                            }
                        }
                    }
                }
            }
        }
    }
}